#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

from common.common import check_command_injection_ex
from common.const import ExecuteResultEnum, ParamConstant, CMDResultInt
from common.exception.common_exception import ErrCodeException
from common.logger import Logger
from common.util.exec_utils import su_exec_rm_cmd
from informix.comm.comm import informix_open_log_backup, get_db_file_size, output_execution_result_ex, \
    read_tmp_json_file, umount, execute_cmd, identify_env_user
from informix.comm.common_models import ActionResult
from informix.comm.const import JsonConstant, InformixInfo
from informix.comm.const import StatusOrVersion
from informix.service.BaseService import MetaServiceWorker
from informix.service.resource.resource_service import InformixInfos

log = Logger().get_logger("informix.log")


class Resource(MetaServiceWorker):
    check_output = ActionResult(code=ExecuteResultEnum.SUCCESS)

    def __init__(self, job_manager, param):
        super().__init__(job_manager, param)
        self.param = param
        self.pid = job_manager.pid
        self.job_id = job_manager.job_id
        self.open_log = self.param.get_open_log()
        self.sub_type = self.param.get_sub_type()
        self.informix_infos_obj = InformixInfos(self.param)
        self.check_output.code = 0
        self.db_user = identify_env_user() if identify_env_user() else InformixInfo.INFORMIX_USER

    def check_application(self):
        """
        连通性校验
        1. 检查数据库是否安装
        2. informix用户是否存在
        3. 检查数据库实例是否存在
            3.1 检查配置文件是否存在
            3.2 检查文件中实例名与参数中的实例名是否匹配
        4. 检查实例状态
        :return: 无返回值，结果直接写入文件中
        """
        log.info("Enter CheckApplication service.")
        if self.sub_type == JsonConstant.INFORMIX_SERVICE:
            error_code = self.informix_infos_obj.check_cluster()
            if error_code != ExecuteResultEnum.SUCCESS:
                log.error("Failed to check application. Error: %s", error_code)
                raise ErrCodeException(error_code)
            return True
        self.update_file_size()
        file_size = self.parse_size_json()
        self.check_output.code = ExecuteResultEnum.SUCCESS
        error_code = self.informix_infos_obj.check_app()
        if error_code != ExecuteResultEnum.SUCCESS:
            log.error("Failed to check application. Error: %s", error_code)
            raise ErrCodeException(error_code)
        self.return_result = ActionResult(0, message=str(file_size))
        self.update_result()
        return True

    def regist_server(self):
        if not self.open_log:
            return True
        ret, out, err = informix_open_log_backup(self.db_user)
        if ret != "0":
            log.error("Failed to enable log backup.. Error: %s", ret)
            error_code = 1
            raise ErrCodeException(error_code)
        return True

    def support_informix_resource(self):
        """
        注册数据库实例，上报SERVERNUM、informix版本信息。
        1. 连通性检查
        2. 区分单机和集群状态，完成信息上报：
            单机：校验节点的ip（sqlhosts）是否包含于agentIpList
            集群：
                主节点：
                    校验主节点的ip（sqlhosts）是否包含于agentIpList
                    查询备节点实例名，校验备节点ip（sqlhosts）是否包含于agentIpList
                备节点：
                    校验备节点的ip（sqlhosts）是否包含于agentIpList
                    查询主节点实例名，校验主节点ip（sqlhosts）是否包含于agentIpList
        3. 注意是怎么样实现信息上报的，即上报信息的json格式是什么样的
        :return:
        """
        log.info("Enter ListApplicationResource service.")
        self.set_result({})
        error_code = self.informix_infos_obj.check_app()
        if error_code != ExecuteResultEnum.SUCCESS:
            log.error("The status of current node is not on-line, and could not support informix resource.")
            self.update_result()
            return
        info = (self.informix_infos_obj.instance_name_curr, '')
        if self.informix_infos_obj.is_cluster:
            info = self.informix_infos_obj.check_instance_ip()
            if not all(info):
                log.error("Failed to get ip info.")
                self.update_result()
                return
        status = InformixInfos.get_informix_version_or_status(self.informix_infos_obj.custom_dict_curr,
                                                              StatusOrVersion.VERSION)
        if not status:
            log.error("Failed to pre version.")
            self.update_result()
            return
        server_num, application_version = self.informix_infos_obj.get_support_info()
        if not (server_num and application_version):
            log.error("Failed to get SERVERNUM or the version of Informix.")
            self.update_result()
            return
        status = InformixInfos.get_informix_version_or_status(self.informix_infos_obj.custom_dict_curr,
                                                              StatusOrVersion.STATUS)
        root_dbs_file_path = InformixInfos.get_root_dbs_file_path(self.informix_infos_obj.onconfig_file_curr)
        if not root_dbs_file_path:
            log.error("Failed to get rootdbs of Informix.")
            self.update_result()
            return
        app_env = self.param.get_appenv()
        env_info = {
            "serverNum": server_num,
            "applicationVersion": application_version,
            "instanceStatus": status,
            "pairedServer": info[0],
            'rootdbsPath': root_dbs_file_path,
            "localServer": self.informix_infos_obj.instance_name_curr,
            "pairedServerIp": info[1]
        }
        log.info(f"env_info:{env_info}")
        app_env["extendInfo"].update(env_info)
        self.set_result({"resourceList": [app_env]})
        self.update_result()
        return

    def update_file_size(self):
        root_dbs_file_path = InformixInfos.get_root_dbs_file_path(self.informix_infos_obj.onconfig_file_curr)
        try:
            file_size = get_db_file_size(root_dbs_file_path)
        except Exception as e_obj:
            file_size = 0
            log.warning(f'Fail to get rootdbs size, err: {e_obj}.')
        data_name_size_path = os.path.join(ParamConstant.PARAM_FILE_PATH,
                                           f"{self.informix_infos_obj.instance_name_curr}_data_size")
        ret, size_info = self.check_size_json(data_name_size_path)
        if ret:
            size_info[0][self.informix_infos_obj.instance_name_curr] = file_size
        else:
            size_info = [{self.informix_infos_obj.instance_name_curr: file_size}]
        try:
            output_execution_result_ex(data_name_size_path, size_info)
        except Exception as e_obj:
            log.debug(f'Fail to save size, err: {e_obj}.')
        return

    def parse_size_json(self):
        data_name_size_path = os.path.join(ParamConstant.PARAM_FILE_PATH,
                                           f"{self.informix_infos_obj.instance_name_curr}_data_size")
        result_size = 0
        ret, size_info = self.check_size_json(data_name_size_path)
        if not ret:
            return result_size
        if not size_info:
            return result_size
        if size_info[0].get("reserved_size", 0):
            return size_info[0].get("reserved_size", 0)
        file_size = size_info[0].get(self.informix_infos_obj.instance_name_curr, 0)
        if not isinstance(file_size, int):
            return result_size
        result_size = int(file_size) * 1.5
        return int(result_size)

    def check_size_json(self, data_name_size_path):
        if os.path.exists(data_name_size_path):
            try:
                size_info = read_tmp_json_file(data_name_size_path)
            except Exception as e_obj:
                log.exception(f'Fail to get rootdbs size, err: {e_obj}, job_id: {self.job_id}.')
                return False, {}
            if isinstance(size_info, list):
                if size_info and isinstance(size_info[0], dict):
                    return True, size_info
        return False, {}

    def remove_protect(self):
        log.info("Enter RemoveProtect service.")
        app_env = self.param.get_appenv()
        log_path = app_env.get(JsonConstant.EXTEND_INFO, {}).get(JsonConstant.LOG_BACKUP_PATH, "")
        application = self.param.get_application()
        if self.informix_infos_obj.is_cluster:
            resource_id = application.get(JsonConstant.PARENTID, "")
        else:
            resource_id = application.get(JsonConstant.ID, "")
        instance_name = application.get(JsonConstant.NAME, "")
        local_log_path = os.path.join(log_path, resource_id, "data", "1", instance_name)
        log.info("Umount local log path: %s", local_log_path)
        if umount(local_log_path):
            onbar_process_file = os.path.join(log_path, resource_id, "onbar_process_info")
            log_resource_path = os.path.join(log_path, resource_id)
            if check_command_injection_ex(onbar_process_file) or check_command_injection_ex(log_resource_path):
                log.error("Umount local log path is invalid.")
                self.set_result(ActionResult(CMDResultInt.FAILED))
                self.update_result()
                return True
            su_exec_rm_cmd(onbar_process_file)
            su_exec_rm_cmd(log_resource_path, "informix")
        return_result = ActionResult(CMDResultInt.SUCCESS)
        self.set_result(return_result)
        self.update_result()
        return True
